

<!DOCTYPE html>
<html lang="zh-CN" data-default-color-scheme=&#34;auto&#34;>



<head>
  <meta charset="UTF-8">
  <link rel="apple-touch-icon" sizes="76x76" href="/img/favicon.png">
  <link rel="icon" href="/img/favicon.png">
  <meta name="viewport"
        content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, shrink-to-fit=no">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  
  <meta name="theme-color" content="#2f4154">
  <meta name="description" content="一： MySQL执行外部sql脚本文件的命令sql脚本是包含一到多个sql命令的sql语句，我们可以将这些sql脚本放在一个文本文件中（我们称之为“sql脚本文件”），然后通过相关的命令执行这个sql脚本文件。基本步骤如下：  
1.1 创建包含sql命令的sql脚本文件文件中包含一些列的sql语句，每条语句最后以;结尾，文件内容示例如下：–创建表，使用“–”进行注释
123456create t">
  <meta name="author" content="closer">
  <meta name="keywords" content="">
  
  <title>缪雪峰MySQL教程 - closer的自留地</title>

  <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" />


  <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/github-markdown-css@4.0.0/github-markdown.min.css" />
  <link  rel="stylesheet" href="/lib/hint/hint.min.css" />

  
    
    
      
      <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@10.6.0/styles/androidstudio.min.css" />
    
  

  
    <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.css" />
  



<!-- 主题依赖的图标库，不要自行修改 -->

<link rel="stylesheet" href="//at.alicdn.com/t/font_1749284_ba1fz6golrf.css">



<link rel="stylesheet" href="//at.alicdn.com/t/font_1736178_kmeydafke9r.css">


<link  rel="stylesheet" href="/css/main.css" />

<!-- 自定义样式保持在最底部 -->


  <script id="fluid-configs">
    var Fluid = window.Fluid || {};
    var CONFIG = {"hostname":"blog.zsaa.top","root":"/","version":"1.8.10","typing":{"enable":true,"typeSpeed":70,"cursorChar":"_","loop":false},"anchorjs":{"enable":true,"element":"h1,h2,h3,h4,h5,h6","placement":"right","visible":"always","icon":""},"progressbar":{"enable":true,"height_px":3,"color":"#29d","options":{"showSpinner":false,"trickleSpeed":100}},"copy_btn":true,"image_zoom":{"enable":true,"img_url_replace":["",""]},"toc":{"enable":true,"headingSelector":"h1,h2,h3,h4,h5,h6","collapseDepth":0},"lazyload":{"enable":true,"loading_img":"/img/loading.gif","onlypost":false,"offset_factor":2},"web_analytics":{"enable":true,"baidu":"608f2baddd361128381ad2bf9377bf89","google":null,"gtag":null,"tencent":{"sid":null,"cid":null},"woyaola":null,"cnzz":null,"leancloud":{"app_id":"YzLqNtMw1YEwwACli1FUsIUM-gzGzoHsz","app_key":"HLUt5izfTvTcbEbOrA59W92a","server_url":"https://yzlqntmw.lc-cn-n1-shared.com"}}};
  </script>
  <script  src="/js/utils.js" ></script>
  <script  src="/js/color-schema.js" ></script>
<meta name="generator" content="Hexo 5.4.0"></head>


<body>
  <header style="height: 70vh;">
    <nav id="navbar" class="navbar fixed-top  navbar-expand-lg navbar-dark scrolling-navbar">
  <div class="container">
    <a class="navbar-brand"
       href="/">&nbsp;<strong>Hello</strong>&nbsp;</a>

    <button id="navbar-toggler-btn" class="navbar-toggler" type="button" data-toggle="collapse"
            data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <div class="animated-icon"><span></span><span></span><span></span></div>
    </button>

    <!-- Collapsible content -->
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav ml-auto text-center">
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/">
                <i class="iconfont icon-home-fill"></i>
                首页
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/archives/">
                <i class="iconfont icon-archive-fill"></i>
                归档
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/categories/">
                <i class="iconfont icon-category-fill"></i>
                分类
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/tags/">
                <i class="iconfont icon-tags-fill"></i>
                标签
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/about/">
                <i class="iconfont icon-user-fill"></i>
                关于
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/links/">
                <i class="iconfont icon-link-fill"></i>
                友链
              </a>
            </li>
          
        
        
          <li class="nav-item" id="search-btn">
            <a class="nav-link" target="_self" data-toggle="modal" data-target="#modalSearch">&nbsp;<i
                class="iconfont icon-search"></i>&nbsp;</a>
          </li>
        
        
          <li class="nav-item" id="color-toggle-btn">
            <a class="nav-link" target="_self">&nbsp;<i
                class="iconfont icon-dark" id="color-toggle-icon"></i>&nbsp;</a>
          </li>
        
      </ul>
    </div>
  </div>
</nav>

    <div class="banner" id="banner" parallax=true
         style="background: url('/img/default.jpg') no-repeat center center;
           background-size: cover;">
      <div class="full-bg-img">
        <div class="mask flex-center" style="background-color: rgba(0, 0, 0, 0.3)">
          <div class="page-header text-center fade-in-up">
            <span class="h2" id="subtitle" title="缪雪峰MySQL教程">
              
            </span>

            
              <div class="mt-3">
  
  
    <span class="post-meta">
      <i class="iconfont icon-date-fill" aria-hidden="true"></i>
      <time datetime="2020-01-24 17:07" pubdate>
        2020年1月24日 下午
      </time>
    </span>
  
</div>

<div class="mt-1">
  
    
    <span class="post-meta mr-2">
      <i class="iconfont icon-chart"></i>
      12.5k 字
    </span>
  

  
    
    <span class="post-meta mr-2">
      <i class="iconfont icon-clock-fill"></i>
      
      
      145
       分钟
    </span>
  

  
  
    
      <!-- LeanCloud 统计文章PV -->
      <span id="leancloud-page-views-container" class="post-meta" style="display: none">
        <i class="iconfont icon-eye" aria-hidden="true"></i>
        <span id="leancloud-page-views"></span> 次
      </span>
    
  
</div>

            
          </div>

          
        </div>
      </div>
    </div>
  </header>

  <main>
    
      

<div class="container-fluid nopadding-x">
  <div class="row nomargin-x">
    <div class="d-none d-lg-block col-lg-2"></div>
    <div class="col-lg-8 nopadding-x-md">
      <div class="container nopadding-x-md" id="board-ctn">
        <div class="py-5" id="board">
          <article class="post-content mx-auto">
            <!-- SEO header -->
            <h1 style="display: none">缪雪峰MySQL教程</h1>
            
              <p class="note note-info">
                
                  本文最后更新于：2020年2月19日 晚上
                
              </p>
            
            <div class="markdown-body">
              <h1 id="一：-MySQL执行外部sql脚本文件的命令"><a href="#一：-MySQL执行外部sql脚本文件的命令" class="headerlink" title="一： MySQL执行外部sql脚本文件的命令"></a>一： MySQL执行外部sql脚本文件的命令</h1><p>sql脚本是包含一到多个sql命令的sql语句，我们可以将这些sql脚本放在一个文本文件中（我们称之为“sql脚本文件”），然后通过相关的命令执行这个sql脚本文件。基本步骤如下：  </p>
<h2 id="1-1-创建包含sql命令的sql脚本文件"><a href="#1-1-创建包含sql命令的sql脚本文件" class="headerlink" title="1.1 创建包含sql命令的sql脚本文件"></a>1.1 创建包含sql命令的sql脚本文件</h2><p>文件中包含一些列的sql语句，每条语句最后以;结尾，文件内容示例如下：<br>–创建表，使用“–”进行注释</p>
<figure class="highlight sql"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs sql"><span class="hljs-keyword">create</span> <span class="hljs-keyword">table</span> 表名称<br>(<br>  Guid <span class="hljs-type">Varchar</span>(<span class="hljs-number">38</span>) <span class="hljs-keyword">not</span> <span class="hljs-keyword">null</span> <span class="hljs-keyword">primary</span> key,  <br>  Title <span class="hljs-type">Varchar</span>(<span class="hljs-number">255</span>),<br><br>) TYPE<span class="hljs-operator">=</span>InnoDB;<br></code></pre></div></td></tr></table></figure>
<span id="more"></span>
<ul>
<li>在表A中增加字段Status<br><code>alter table A add Status TinyInt default &#39;0&#39;;</code></li>
<li>在表A上创建索引<br><code>create index XX_TaskId_1 on A(Id_);</code></li>
<li>在A表中添加一条记录<br><code>Insert into A (Id,ParentId, Name) values(1,0,&#39;名称&#39;);</code></li>
<li>添加、修改、删除数据后，有可能需要提交事务<br><code>Commit;</code></li>
</ul>
<h2 id="1-2-执行sql脚本文件"><a href="#1-2-执行sql脚本文件" class="headerlink" title="1.2 执行sql脚本文件"></a>1.2 执行sql脚本文件</h2><h3 id="1-2-1-方法一-：使用cmd命令执行-windows下，unix或linux在的其控制台下"><a href="#1-2-1-方法一-：使用cmd命令执行-windows下，unix或linux在的其控制台下" class="headerlink" title="1.2.1 方法一 ：使用cmd命令执行(windows下，unix或linux在的其控制台下)"></a>1.2.1 方法一 ：使用cmd命令执行(windows下，unix或linux在的其控制台下)</h3><p>【Mysql的bin目录】\mysql –u用户名 –p密码 –D数据库&lt;【sql脚本文件路径全名】，示例：<br><code>D:\mysql\bin\mysql –uroot –p123456 -Dtest&lt;d:\test\ss.sql</code><br>注意：<br>A、如果在sql脚本文件中使用了use 数据库，则-D数据库选项可以忽略<br>B、如果【Mysql的bin目录】中包含空格，则需要使用“”包含，如：<code>“C:\Program Files\mysql\bin\mysql” –u用户名 –p密码 –D数据库&lt;【sql脚本文件路径全名】</code></p>
<h3 id="1-2-2-方法二-：进入mysql的控制台后，使用source命令执行"><a href="#1-2-2-方法二-：进入mysql的控制台后，使用source命令执行" class="headerlink" title="1.2.2 方法二 ：进入mysql的控制台后，使用source命令执行"></a>1.2.2 方法二 ：进入mysql的控制台后，使用source命令执行</h3><p>Mysql&gt;source 【sql脚本文件的路径全名】 或 Mysql&gt;. 【sql脚本文件的路径全名】，示例：<code>source d:\test\ss.sql</code> 或者 <code>\. d:\test\ss.sql</code></p>
<h1 id="二：-关系数据库概述"><a href="#二：-关系数据库概述" class="headerlink" title="二： 关系数据库概述"></a>二： 关系数据库概述</h1><p>数据库按照数据结构来组织、存储和管理数据，实际上，数据库一共有三种模型：  </p>
<ul>
<li>层次模型</li>
<li>网状模型</li>
<li>关系模型</li>
</ul>
<p>随着时间的推移和市场竞争，最终，基于<strong>关系模型</strong>的关系数据库获得了绝对市场份额。  </p>
<h2 id="2-1-数据类型"><a href="#2-1-数据类型" class="headerlink" title="2.1 数据类型"></a>2.1 数据类型</h2><p>对于一个关系表，除了定义每一列的名称外，还需要定义每一列的数据类型。关系数据库支持的标准数据类型包括数值、字符串、时间等：  </p>
<table>
<thead>
<tr>
<th>名称</th>
<th>类型</th>
<th>说明</th>
</tr>
</thead>
<tbody><tr>
<td>INT</td>
<td>整型</td>
<td>4字节整数类型，范围约+/-21亿</td>
</tr>
<tr>
<td>BIGINT</td>
<td>长整型</td>
<td>8字节整数类型，范围约+/-922亿亿</td>
</tr>
<tr>
<td>REAL</td>
<td>浮点型</td>
<td>4字节浮点数，范围约+/-1038</td>
</tr>
<tr>
<td>DOUBLE</td>
<td>浮点型</td>
<td>8字节浮点数，范围约+/-10308</td>
</tr>
<tr>
<td>DECIMAL(M,N)</td>
<td>高精度小数</td>
<td>由用户指定精度的小数，例如，DECIMAL(20,10)表示一共20位，其中小数10位，通常用于财务计算</td>
</tr>
<tr>
<td>CHAR(N)</td>
<td>定长字符串</td>
<td>存储指定长度的字符串，例如，CHAR(100)总是存储100个字符的字符串</td>
</tr>
<tr>
<td>VARCHAR(N)</td>
<td>变长字符串</td>
<td>存储可变长度的字符串，例如，VARCHAR(100)可以存储0~100个字符的字符串</td>
</tr>
<tr>
<td>BOOLEAN</td>
<td>布尔类型</td>
<td>存储True或者False</td>
</tr>
<tr>
<td>DATE</td>
<td>日期类型</td>
<td>存储日期，例如，2018-06-22</td>
</tr>
<tr>
<td>TIME</td>
<td>时间类型</td>
<td>存储时间，例如，12:20:59</td>
</tr>
<tr>
<td>DATETIME</td>
<td>日期和时间类型</td>
<td>存储日期+时间，例如，2018-06-22 12:20:59</td>
</tr>
</tbody></table>
<h2 id="2-2-SQL-DML-和-DDL"><a href="#2-2-SQL-DML-和-DDL" class="headerlink" title="2.2 SQL DML 和 DDL"></a>2.2 SQL DML 和 DDL</h2><p>总的来说，可以把 SQL 分为两个部分：数据操作语言 (DML) 和 数据定义语言 (DDL)。<br>SQL (结构化查询语言)是用于执行查询的语法。但是 SQL 语言也包含用于更新、插入和删除记录的语法。<br>查询和更新指令构成了 SQL 的 DML 部分：  </p>
<ol>
<li>SELECT - 从数据库表中获取数据</li>
<li>UPDATE - 更新数据库表中的数据</li>
<li>DELETE - 从数据库表中删除数据</li>
<li>INSERT INTO - 向数据库表中插入数据</li>
</ol>
<p>SQL 的数据定义语言 (DDL) 部分使我们有能力创建或删除表格。我们也可以定义索引（键），规定表之间的链接，以及施加表间的约束。<br>SQL 中最重要的 DDL 语句:  </p>
<ol>
<li>CREATE DATABASE - 创建新数据库</li>
<li>ALTER DATABASE - 修改数据库</li>
<li>CREATE TABLE - 创建新表</li>
<li>ALTER TABLE - 变更（改变）数据库表</li>
<li>DROP TABLE - 删除表</li>
<li>CREATE INDEX - 创建索引（搜索键）</li>
<li>DROP INDEX - 删除索引</li>
</ol>
<h2 id="2-3-安装MySQL"><a href="#2-3-安装MySQL" class="headerlink" title="2.3 安装MySQL"></a>2.3 安装MySQL</h2><p>要在Windows或Mac上安装MySQL，首先从MySQL官方网站下载最新的MySQL Community Server版本：<br><a target="_blank" rel="noopener" href="https://dev.mysql.com/downloads/mysql/">MySQL官方网站</a><br>选择对应的操作系统版本，下载安装即可。在安装过程中，MySQL会自动创建一个root用户，并提示输入root口令。<br>要在Linux上安装MySQL，可以使用发行版的包管理器。例如，Debian和Ubuntu用户可以简单地通过命令<code>apt-get install mysql-server</code>安装最新的MySQL版本。  </p>
<h2 id="2-4-运行MySQL"><a href="#2-4-运行MySQL" class="headerlink" title="2.4 运行MySQL"></a>2.4 运行MySQL</h2><p>MySQL安装后会自动在后台运行。为了验证MySQL安装是否正确，我们需要通过mysql这个命令行程序来连接MySQL服务器。<br>在命令提示符下输入mysql -u root -p，然后输入口令，如果一切正确，就会连接到MySQL服务器，同时提示符变为mysql&gt;。<br>输入exit退出MySQL命令行。注意，MySQL服务器仍在后台运行。  </p>
<h1 id="三：-关系模型"><a href="#三：-关系模型" class="headerlink" title="三： 关系模型"></a>三： 关系模型</h1><p>我们已经知道，关系数据库是建立在关系模型上的。而关系模型本质上就是若干个存储数据的二维表，可以把它们看作很多Excel表。<br>表的每一行称为记录（Record），记录是一个逻辑意义上的数据。<br>表的每一列称为字段（Column），同一个表的每一行记录都拥有相同的若干字段。<br>字段定义了数据类型（整型、浮点型、字符串、日期等），以及是否允许为NULL。注意NULL表示字段数据不存在。一个整型字段如果为NULL不表示它的值为0，同样的，一个字符串型字段为NULL也不表示它的值为空串’’。  </p>
<blockquote>
<p>通常情况下，字段应该避免允许为NULL。不允许为NULL可以简化查询条件，加快查询速度，也利于应用程序读取数据后无需判断是否为NULL。  </p>
</blockquote>
<p>在关系数据库中，关系是通过主键和外键来维护的。</p>
<h2 id="3-1-主键"><a href="#3-1-主键" class="headerlink" title="3.1 主键"></a>3.1 主键</h2><p>在关系数据库中，一张表中的每一行数据被称为一条记录。一条记录就是由多个字段组成的。<br>每一条记录都包含若干定义好的字段。同一个表的所有记录都有相同的字段定义。<br><strong>对于关系表，有个很重要的约束，就是任意两条记录不能重复。不能重复不是指两条记录不完全相同，而是指能够通过某个字段唯一区分出不同的记录，这个字段被称为主键。</strong><br>对主键的要求，最关键的一点是：记录一旦插入到表中，主键最好不要再修改，因为主键是用来唯一定位记录的，修改了主键，会造成一系列的影响。<br>由于主键的作用十分重要，如何选取主键会对业务开发产生重要影响。如果我们以学生的身份证号作为主键，似乎能唯一定位记录。然而，身份证号也是一种业务场景，如果身份证号升位了，或者需要变更，作为主键，不得不修改的时候，就会对业务产生严重影响。<br>所以，选取主键的一个基本原则是：不使用任何业务相关的字段作为主键。<br>因此，身份证号、手机号、邮箱地址这些看上去可以唯一的字段，均不可用作主键。<br>作为主键最好是完全业务无关的字段，我们一般把这个字段命名为id。常见的可作为id字段的类型有：  </p>
<ul>
<li>自增整数类型：数据库会在插入数据时自动为每一条记录分配一个自增整数，这样我们就完全不用担心主键重复，也不用自己预先生成主键；</li>
<li>全局唯一GUID类型：使用一种全局唯一的字符串作为主键，类似8f55d96b-8acc-4636-8cb8-76bf8abc2f57。GUID算法通过网卡MAC地址、时间戳和随机数保证任意计算机在任意时间生成的字符串都是不同的，大部分编程语言都内置了GUID算法，可以自己预算出主键。</li>
</ul>
<p>对于大部分应用来说，通常自增类型的主键就能满足需求。  </p>
<blockquote>
<p>PS:如果使用INT自增类型，那么当一张表的记录数超过2147483647（约21亿）时，会达到上限而出错。使用BIGINT自增类型则可以最多约922亿亿条记录。  </p>
</blockquote>
<p>关系数据库实际上还允许通过多个字段唯一标识记录，即两个或更多的字段都设置为主键，这种主键被称为<strong>联合主键</strong>。<br>对于联合主键，允许一列有重复，只要不是所有主键列都重复即可。<br>没有必要的情况下，我们尽量不使用联合主键，因为它给关系表带来了复杂度的上升。  </p>
<p><strong>小结</strong><br><strong>主键是关系表中记录的唯一标识。主键的选取非常重要：主键不要带有业务含义，而应该使用<code>BIGINT自增</code>或者<code>GUID类型</code>。主键也不应该允许<code>NULL</code>。可以使用多个列作为联合主键，但联合主键并不常用。</strong></p>
<h2 id="3-2-外键"><a href="#3-2-外键" class="headerlink" title="3.2 外键"></a>3.2 外键</h2><p>由于一个班级可以有多个学生，在关系模型中，这两个表的关系可以称为“一对多”，即一个<code>classes</code>的记录可以对应多个<code>students</code>表的记录。<br>为了表达这种一对多的关系，我们需要在<code>students</code>表中加入一列<code>class_id</code>，让它的值与<code>classes</code>表的某条记录相对应。<br>这样，我们就可以根据<code>class_id</code>这个列直接定位出一个<code>students</code>表的记录应该对应到<code>classes</code>的哪条记录。<br>在<code>students</code>表中，通过<code>class_id</code>的字段，可以把数据与另一张表关联起来，这种列称为外键。<br>外键并不是通过列名实现的，而是通过定义外键约束实现的：</p>
<figure class="highlight sql"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs sql"><span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> students<br><span class="hljs-keyword">ADD</span> <span class="hljs-keyword">CONSTRAINT</span> fk_class_id<br><span class="hljs-keyword">FOREIGN</span> KEY (class_id)<br><span class="hljs-keyword">REFERENCES</span> classes (id);<br></code></pre></div></td></tr></table></figure>

<p>其中，外键约束的名称<code>fk_class_id</code>可以任意，<code>FOREIGN KEY (class_id)</code>指定了<code>class_id</code>作为外键，<code>REFERENCES classes (id)</code>指定了这个外键将关联到<code>classes</code>表的id列（即<code>classes</code>表的主键）。<br>通过定义外键约束，关系数据库可以保证无法插入无效的数据。即如果<code>classes</code>表不存在id=99的记录，<code>students</code>表就无法插入<code>class_id=99</code>的记录。<br>由于外键约束会降低数据库的性能，大部分互联网应用程序为了追求速度，并不设置外键约束，而是仅靠应用程序自身来保证逻辑的正确性。这种情况下，<code>class_id</code>仅仅是一个普通的列，只是它起到了外键的作用而已。<br>要删除一个外键约束，也是通过<code>ALTER TABLE</code>实现的：</p>
<figure class="highlight sql"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs sql"><span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> students<br><span class="hljs-keyword">DROP</span> <span class="hljs-keyword">FOREIGN</span> KEY fk_class_id;<br></code></pre></div></td></tr></table></figure>

<blockquote>
<p>注意：删除外键约束并没有删除外键这一列。删除列是通过<code>DROP COLUMN ...</code>实现的。  </p>
</blockquote>
<p>通过一个表的外键关联到另一个表，我们可以定义出一对多关系。有些时候，还需要定义“多对多”关系。例如，一个老师可以对应多个班级，一个班级也可以对应多个老师，因此，班级表和老师表存在多对多关系。<br>多对多关系实际上是通过两个一对多关系实现的，即<strong>通过一个中间表，关联两个一对多关系，就形成了多对多关系</strong>。<br>一对一关系是指，一个表的记录对应到另一个表的唯一一个记录。  目的是把经常读取和不经常读取的字段分开，提高查询速度，以获得更高的性能。  </p>
<p><strong>小结</strong><br><strong>关系数据库通过外键可以实现一对多、多对多和一对一的关系。外键既可以通过数据库来约束，也可以不设置约束，仅依靠应用程序的逻辑来保证。</strong></p>
<h2 id="3-3-索引"><a href="#3-3-索引" class="headerlink" title="3.3 索引"></a>3.3 索引</h2><p>在关系数据库中，如果有上万甚至上亿条记录，在查找记录的时候，想要获得非常快的速度，就需要使用索引。<br>索引是关系数据库中对某一列或多个列的值进行预排序的数据结构。通过使用索引，可以让数据库系统不必扫描整个表，而是直接定位到符合条件的记录，这样就大大加快了查询速度。<br>如果要经常根据<code>score</code>列进行查询，就可以对<code>score</code>列创建索引：</p>
<figure class="highlight sql"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs sql"><span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> students<br><span class="hljs-keyword">ADD</span> INDEX idx_score (score);<br></code></pre></div></td></tr></table></figure>

<p>使用<code>ADD INDEX idx_score (score)</code>就创建了一个名称为<code>idx_score</code>，使用列<code>score</code>的索引。索引名称是任意的，索引如果有多列，可以在括号里依次写上，例如：</p>
<figure class="highlight sql"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs sql"><span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> students<br><span class="hljs-keyword">ADD</span> INDEX idx_name_score (name, score);<br></code></pre></div></td></tr></table></figure>

<p>索引的效率取决于索引列的值是否散列，即该列的值如果越互不相同，那么索引效率越高。反过来，如果记录的列存在大量相同的值，例如<code>gender</code>列，大约一半的记录值是M，另一半是F，因此，对该列创建索引就没有意义。<br>可以对一张表创建多个索引。索引的优点是提高了查询效率，缺点是在插入、更新和删除记录时，需要同时修改索引，因此，索引越多，插入、更新和删除记录的速度就越慢。<br>对于主键，关系数据库会自动对其创建主键索引。使用主键索引的效率是最高的，因为主键会保证绝对唯一。  </p>
<p><strong>小结</strong><br><strong>通过对数据库表创建索引，可以提高查询速度。</strong><br><strong>通过创建唯一索引，可以保证某一列的值具有唯一性。</strong><br><strong>数据库索引对于用户和应用程序来说都是透明的。</strong></p>
<h1 id="四：-查询数据"><a href="#四：-查询数据" class="headerlink" title="四： 查询数据"></a>四： 查询数据</h1><p>在关系数据库中，最常用的操作就是查询。</p>
<h2 id="4-1-基本查询"><a href="#4-1-基本查询" class="headerlink" title="4.1 基本查询"></a>4.1 基本查询</h2><p>要查询数据库表的数据，我们使用如下的SQL语句：<br><code>SELECT * FROM &lt;表名&gt;</code><br>假设表名是<code>students</code>，要查询<code>students</code>表的所有行，我们用如下SQL语句：<br><code>SELECT * FROM students;</code>。<br>使用<code>SELECT * FROM students</code>时，<code>SELECT</code>是关键字，表示将要执行一个查询，<code>*</code>表示“所有列”，<code>FROM</code>表示将要从哪个表查询，本例中是<code>students</code>表。<br>该SQL将查询出<code>students</code>表的所有数据。注意：查询结果也是一个二维表，它包含列名和每一行的数据。  </p>
<p>在表中，可能会包含重复值。这并不成问题，不过，有时您也许希望仅仅列出不同（distinct）的值。<br>关键词 DISTINCT 用于返回唯一不同的值。语法：</p>
<figure class="highlight sql"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">DISTINCT</span> 列名称 <span class="hljs-keyword">FROM</span> 表名称<br></code></pre></div></td></tr></table></figure>

<p><strong>小结</strong><br><strong>使用<code>SELECT(选择、挑选、选拔)</code>查询的基本语句<code>SELECT * FROM &lt;表名&gt;</code>可以查询一个表的所有行和所有列的数据。<code>SELECT</code>查询的结果是一个二维表。</strong>  </p>
<h2 id="4-2-条件查询"><a href="#4-2-条件查询" class="headerlink" title="4.2 条件查询"></a>4.2 条件查询</h2><p><code>SELECT</code>语句可以通过<code>WHERE（到哪里）</code>条件来设定查询条件，查询结果是满足查询条件的记录。例如，要指定条件“分数在80分或以上的学生”，写成<code>WHERE</code>条件就是:<br><code>SELECT * FROM students WHERE score &gt;= 80;</code>。<br>其中，<code>WHERE</code>关键字后面的<code>score &gt;= 80</code>就是条件。<code>score</code>是列名，该列存储了学生的成绩，因此，<code>score &gt;= 80</code>就筛选出了指定条件的记录：<br><code>SELECT * FROM students WHERE score &gt;= 80;</code>。<br>因此，条件查询的语法就是：<br><code>SELECT * FROM &lt;表名&gt; WHERE &lt;条件表达式&gt;</code>  </p>
<p>条件表达式可以用<code>&lt;条件1&gt; AND &lt;条件2&gt;</code>表达满足条件1并且满足条件2。例如，符合条件“分数在80分或以上”，并且还符合条件“男生”，把这两个条件写出来：</p>
<ul>
<li>条件1：根据score列的数据判断：score &gt;= 80；</li>
<li>条件2：根据gender列的数据判断：gender = ‘M’，注意gender列存储的是字符串，需要用单引号括起来。</li>
</ul>
<p>就可以写出<code>WHERE</code>条件：<br><code>SELECT * FROM students WHERE score &gt;= 80 AND gender = &#39;M&#39;;</code>。<br>第二种条件是&lt;条件1&gt; OR &lt;条件2&gt;，表示满足条件1或者满足条件2。例如：<br><code>SELECT * FROM students WHERE score &gt;= 80 OR gender = &#39;M&#39;;</code><br>第三种条件是NOT &lt;条件&gt;，表示“不符合该条件”的记录。例如：<br><code>SELECT * FROM students WHERE NOT class_id = 2;</code>表示不是2班的学生。<br>要组合三个或者更多的条件，就需要用小括号()表示如何进行条件运算。例如，编写一个复杂的条件：分数在80以下或者90以上，并且是男生：<br><code>SELECT * FROM students WHERE (score &lt; 80 OR score &gt; 90) AND gender = &#39;M&#39;;</code><br>如果不加括号，条件运算按照<code>NOT、AND、OR</code>的优先级进行，即<code>NOT</code>优先级最高，其次是<code>AND</code>，最后是<code>OR</code>。加上括号可以改变优先级。  </p>
<p>常用的条件表达式：  </p>
<table>
<thead>
<tr>
<th>条件</th>
<th>表达式举例1</th>
<th>表达式举例2</th>
<th>说明</th>
</tr>
</thead>
<tbody><tr>
<td>使用=判断相等</td>
<td>score = 80</td>
<td>name = ‘abc’</td>
<td>字符串需要用单引号括起来</td>
</tr>
<tr>
<td>使用&gt;判断大于</td>
<td>score &gt; 80</td>
<td>name &gt; ‘abc’</td>
<td>字符串比较根据ASCII码，中文字符比较根据数据库设置</td>
</tr>
<tr>
<td>使用&gt;=判断大于或相等</td>
<td>score &gt;= 80</td>
<td>name &gt;= ‘abc’</td>
<td></td>
</tr>
<tr>
<td>使用&lt;判断小于</td>
<td>score &lt; 80</td>
<td>name &lt;= ‘abc’</td>
<td></td>
</tr>
<tr>
<td>使用&lt;=判断小于或相等</td>
<td>score &lt;= 80</td>
<td>name &lt;= ‘abc’</td>
<td></td>
</tr>
<tr>
<td>使用&lt;&gt;判断不相等</td>
<td>score &lt;&gt; 80</td>
<td>name &lt;&gt; ‘abc’</td>
<td></td>
</tr>
<tr>
<td>使用LIKE判断相似</td>
<td>name LIKE ‘ab%’</td>
<td>name LIKE ‘%bc%’</td>
<td>%表示任意字符，例如’ab%’将匹配’ab’，’abc’，’abcd’</td>
</tr>
</tbody></table>
<p><strong>小结</strong><br><strong>通过WHERE条件查询，可以筛选出符合指定条件的记录，而不是整个表的所有记录。</strong></p>
<h2 id="4-3-投影查询"><a href="#4-3-投影查询" class="headerlink" title="4.3 投影查询"></a>4.3 投影查询</h2><p>如果我们只希望返回某些列的数据，而不是所有列的数据，我们可以用<code>SELECT 列1, 列2, 列3 FROM ...</code>，让结果集仅包含指定列。这种操作称为投影查询。<br>例如，从<code>students</code>表中返回<code>id、score和name</code>这三列：<br><code>SELECT id, score, name FROM students;</code><br>这样返回的结果集就只包含了我们指定的列，并且，结果集的列的顺序和原表可以不一样。  </p>
<p>使用<code>SELECT 列1, 列2, 列3 FROM ...</code>时，还可以给每一列起个别名，这样，结果集的列名就可以与原表的列名不同。它的语法是<code>SELECT 列1 别名1, 列2 别名2, 列3 别名3 FROM ...</code>。</p>
<p>例如，以下<code>SELECT</code>语句将列名<code>score</code>重命名为<code>points</code>，而<code>id</code>和<code>name</code>列名保持不变：<code>SELECT id, score points, name FROM students;</code><br>投影查询同样可以接<code>WHERE</code>条件，实现复杂的查询：<br><code>SELECT id, score points, name FROM students WHERE gender = &#39;M&#39;;</code>  </p>
<p><strong>小结</strong><br><strong>使用<code>SELECT *</code>表示查询表的所有列，使用<code>SELECT 列1, 列2, 列3</code>则可以仅返回指定列，这种操作称为投影。<code>SELECT</code>语句可以对结果集的列进行重命名。</strong>  </p>
<h2 id="4-4-排序"><a href="#4-4-排序" class="headerlink" title="4.4 排序"></a>4.4 排序</h2><p>可以加上<code>ORDER BY</code>子句。可以按照成绩从低到高进行排序：<br><code>SELECT id, name, gender, score FROM students ORDER BY score;</code><br>如果要反过来，按照成绩从高到底排序，我们可以加上DESC表示“倒序”：<br><code>SELECT id, name, gender, score FROM students ORDER BY score DESC;</code><br>如果score列有相同的数据，要进一步排序，可以继续添加列名。例如，使用ORDER BY score DESC, gender表示先按score列倒序，如果有相同分数的，再按gender列排序：<br><code>SELECT id, name, gender, score FROM students ORDER BY score DESC, gender;</code><br>默认的排序规则是<code>ASC</code>：“升序”，即从小到大。<code>ASC</code>可以省略，即<code>ORDER BY score ASC</code>和<code>ORDER BY score</code>效果一样。<br>如果有<code>WHERE</code>子句，那么<code>ORDER BY</code>子句要放到<code>WHERE</code>子句后面。例如，查询一班的学生成绩，并按照倒序排序：</p>
<figure class="highlight sql"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> id, name, gender, score<br><span class="hljs-keyword">FROM</span> students<br><span class="hljs-keyword">WHERE</span> class_id <span class="hljs-operator">=</span> <span class="hljs-number">1</span><br><span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> score <span class="hljs-keyword">DESC</span>;<br></code></pre></div></td></tr></table></figure>

<p>这样，结果集仅包含符合WHERE条件的记录，并按照ORDER BY的设定排序。<br><strong>小结</strong><br><strong>使用ORDER BY可以对结果集进行排序；可以对多列进行升序、倒序排序。</strong>  </p>
<h2 id="4-5-分页查询"><a href="#4-5-分页查询" class="headerlink" title="4.5 分页查询"></a>4.5 分页查询</h2><p>要实现分页功能，实际上就是从结果集中显示第1<del>100条记录作为第1页，显示第101</del>200条记录作为第2页，以此类推。<br>因此，分页实际上就是从结果集中“截取”出第M~N条记录。这个查询可以通过<code>LIMIT &lt;M&gt; OFFSET &lt;N&gt;</code>子句实现。我们先把所有学生按照成绩从高到低进行排序，我们把结果集分页，每页3条记录。要获取第1页的记录，可以使用<code>LIMIT 3 OFFSET 0</code>：  </p>
<figure class="highlight sql"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> id, name, gender, score<br><span class="hljs-keyword">FROM</span> students<br><span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> score <span class="hljs-keyword">DESC</span><br>LIMIT <span class="hljs-number">3</span> <span class="hljs-keyword">OFFSET</span> <span class="hljs-number">0</span>;<br></code></pre></div></td></tr></table></figure>

<p>上述查询<code>LIMIT 3 OFFSET 0</code>表示，对结果集从0号记录开始，最多取3条。注意SQL记录集的索引从0开始。<br>如果要查询第2页，那么我们只需要“跳过”头3条记录，也就是对结果集从3号记录开始查询，把OFFSET设定为3：<code>LIMIT 3 OFFSET 3;</code><br>LIMIT 3表示的意思是“最多3条记录”。  </p>
<p>可见，分页查询的关键在于，首先要确定每页需要显示的结果数量pageSize（这里是3），然后根据当前页的索引pageIndex（从1开始），确定LIMIT和OFFSET应该设定的值：</p>
<ul>
<li>LIMIT总是设定为pageSize；</li>
<li>OFFSET计算公式为pageSize * (pageIndex - 1)。</li>
</ul>
<p>这样就能正确查询出第N页的记录集。</p>
<blockquote>
<p>PS:OFFSET超过了查询的最大数量并不会报错，而是得到一个空的结果集。<br>OFFSET是可选的，如果只写LIMIT 15，那么相当于LIMIT 15 OFFSET 0。<br>在MySQL中，<code>LIMIT 15 OFFSET 30</code>还可以简写成<code>LIMIT 30, 15</code>。<br>使用<code>LIMIT &lt;M&gt; OFFSET &lt;N&gt;</code>分页时，随着N越来越大，查询效率也会越来越低。  </p>
</blockquote>
<p><strong>小结</strong><br><strong>使用<code>LIMIT &lt;M&gt; OFFSET &lt;N&gt;</code>可以对结果集进行分页，每次查询返回结果集的一部分；分页查询需要先确定每页的数量和当前页数，然后确定LIMIT和OFFSET的值。</strong></p>
<h2 id="4-6-聚合查询"><a href="#4-6-聚合查询" class="headerlink" title="4.6 聚合查询"></a>4.6 聚合查询</h2><h3 id="4-6-1-聚合函数"><a href="#4-6-1-聚合函数" class="headerlink" title="4.6.1 聚合函数"></a>4.6.1 聚合函数</h3><p>对于统计总数、平均数这类计算，SQL提供了专门的聚合函数，使用聚合函数进行查询，就是聚合查询，它可以快速获得结果。<br>仍然以查询students表一共有多少条记录为例，我们可以使用SQL内置的COUNT()函数查询：<br><code>SELECT COUNT(*) FROM students;</code><br><code>COUNT(*)</code>表示查询所有列的行数，要注意聚合的计算结果虽然是一个数字，但查询的结果仍然是一个二维表，只是这个二维表只有一行一列，并且列名是<code>COUNT(*)</code>。<br>通常，使用聚合查询时，我们应该给列名设置一个别名，便于处理结果：<br><code>SELECT COUNT(*) num FROM students;</code><br><code>COUNT(*)</code>和<code>COUNT(id)</code>实际上是一样的效果。另外注意，聚合查询同样可以使用WHERE条件，因此我们可以方便地统计出有多少男生、多少女生、多少80分以上的学生等：<br><code>SELECT COUNT(*) boys FROM students WHERE gender = &#39;M&#39;;</code><br>除了COUNT()函数外，SQL还提供了如下聚合函数：</p>
<table>
<thead>
<tr>
<th>函数</th>
<th>说明</th>
</tr>
</thead>
<tbody><tr>
<td>SUM</td>
<td>计算某一列的合计值，该列必须为数值类型</td>
</tr>
<tr>
<td>AVG</td>
<td>计算某一列的平均值，该列必须为数值类型</td>
</tr>
<tr>
<td>MAX</td>
<td>计算某一列的最大值</td>
</tr>
<tr>
<td>MIN</td>
<td>计算某一列的最小值</td>
</tr>
</tbody></table>
<p>注意，MAX()和MIN()函数并不限于数值类型。如果是字符类型，MAX()和MIN()会返回排序最后和排序最前的字符。<br>要统计男生的平均成绩，我们用下面的聚合查询：<br><code>SELECT AVG(score) average FROM students WHERE gender = &#39;M&#39;;</code><br>要特别注意：如果聚合查询的WHERE条件没有匹配到任何行，COUNT()会返回0，而SUM()、AVG()、MAX()和MIN()会返回NULL。<br>通过聚合查询获得总页数:<br><code>SELECT CEILING(COUNT(*) / 3) FROM students;</code>  </p>
<h3 id="4-6-2-分组聚合"><a href="#4-6-2-分组聚合" class="headerlink" title="4.6.2 分组聚合"></a>4.6.2 分组聚合</h3><p>对于聚合查询，SQL还提供了“分组聚合”的功能。我们观察下面的聚合查询：<br><code>SELECT COUNT(*) num FROM students GROUP BY class_id;</code><br>执行这个查询，COUNT()的结果不再是一个，而是3个，这是因为，<code>GROUP(组) BY</code>子句指定了按class_id分组，因此，执行该SELECT语句时，会把class_id相同的列先分组，再分别计算，因此，得到了3行结果。<br>但是这3行结果分别是哪三个班级的，不好看出来，所以我们可以把class_id列也放入结果集中：<br><code>SELECT class_id, COUNT(*) num FROM students GROUP BY class_id;</code>这下结果集就可以一目了然地看出各个班级的学生人数。<br>也可以使用多个列进行分组。例如，我们想统计各班的男生和女生人数：<br><code>SELECT class_id, gender, COUNT(*) num FROM students GROUP BY class_id, gender;</code><br><strong>小结</strong><br><strong>使用SQL提供的聚合查询，我们可以方便地计算总数、合计值、平均值、最大值和最小值；聚合查询也可以添加WHERE条件。</strong></p>
<h2 id="4-7-多表查询"><a href="#4-7-多表查询" class="headerlink" title="4.7 多表查询"></a>4.7 多表查询</h2><p>SELECT查询不但可以从一张表查询数据，还可以从多张表同时查询数据。查询多张表的语法是：<code>SELECT * FROM &lt;表1&gt; &lt;表2&gt;</code>。<br>例如，同时从students表和classes表的“乘积”，即查询数据，可以这么写：<code>SELECT * FROM students, classes;</code><br>这种一次查询两个表的数据，查询的结果也是一个二维表，它是students表和classes表的“乘积”，即students表的每一行与classes表的每一行都两两拼在一起返回。结果集的列数是students表和classes表的列数之和，行数是students表和classes表的行数之积。<br>这种多表查询又称<strong>笛卡尔查询</strong>，使用笛卡尔查询时要非常小心，由于结果集是目标表的行数乘积，对两个各自有100行记录的表进行笛卡尔查询将返回1万条记录，对两个各自有1万行记录的表进行笛卡尔查询将返回1亿条记录。  </p>
<p>上述查询的结果集有两列id和两列name，两列id是因为其中一列是students表的id，而另一列是classes表的id，但是在结果集中，不好区分。两列name同理。要解决这个问题，我们仍然可以利用投影查询的“设置列的别名”来给两个表各自的id和name列起别名：</p>
<figure class="highlight sql"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br>    students.id sid,<br>    students.name,<br>    students.gender,<br>    students.score,<br>    classes.id cid,<br>    classes.name cname<br><span class="hljs-keyword">FROM</span> students, classes;<br></code></pre></div></td></tr></table></figure>

<p>注意，多表查询时，要使用<code>表名.列名</code>这样的方式来引用列和设置别名，这样就避免了结果集的列名重复问题。但是，用<code>表名.列名</code>这种方式列举两个表的所有列实在是很麻烦，所以SQL还允许给表设置一个别名，让我们在投影查询中引用起来稍微简洁一点：</p>
<figure class="highlight sql"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br>    s.id sid,<br>    s.name,<br>    s.gender,<br>    s.score,<br>    c.id cid,<br>    c.name cname<br><span class="hljs-keyword">FROM</span> students s, classes c;<br></code></pre></div></td></tr></table></figure>

<p>注意到FROM子句给表设置别名的语法是<code>FROM &lt;表名1&gt; &lt;别名1&gt;, &lt;表名2&gt; &lt;别名2&gt;</code>。这样我们用别名s和c分别表示students表和classes表。<br>多表查询也是可以添加WHERE条件的：</p>
<figure class="highlight sql"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br>    s.id sid,<br>    s.name,<br>    s.gender,<br>    s.score,<br>    c.id cid,<br>    c.name cname<br><span class="hljs-keyword">FROM</span> students s, classes c<br><span class="hljs-keyword">WHERE</span> s.gender <span class="hljs-operator">=</span> <span class="hljs-string">&#x27;M&#x27;</span> <span class="hljs-keyword">AND</span> c.id <span class="hljs-operator">=</span> <span class="hljs-number">1</span>;<br></code></pre></div></td></tr></table></figure>

<p><strong>小结</strong><br><strong>使用多表查询可以获取M x N行记录；多表查询的结果集可能非常巨大，要小心使用。</strong></p>
<h2 id="4-8-连接查询"><a href="#4-8-连接查询" class="headerlink" title="4.8 连接查询"></a>4.8 连接查询</h2><p>连接查询是另一种类型的多表查询。连接查询对多个表进行JOIN运算，简单地说，就是先确定一个主表作为结果集，然后，把其他表的行有选择性地“连接”在主表结果集上。<br>例如，我们想要选出students表的所有学生信息，可以用一条简单的SELECT语句完成：  </p>
<figure class="highlight sql"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> s.id, s.name, s.class_id, s.gender, s.score <span class="hljs-keyword">FROM</span> students s;<br></code></pre></div></td></tr></table></figure>

<p>但是，假设我们希望结果集同时包含所在班级的名称，上面的结果集只有class_id列，缺少对应班级的name列。<br>现在问题来了，存放班级名称的name列存储在classes表中，只有根据students表的class_id，找到classes表对应的行，再取出name列，就可以获得班级名称。<br>这时，连接查询就派上了用场。我们先使用最常用的一种内连接——INNER JOIN来实现：  </p>
<figure class="highlight sql"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> s.id, s.name, s.class_id, c.name class_name, s.gender, s.score<br><span class="hljs-keyword">FROM</span> students s<br><span class="hljs-keyword">INNER</span> <span class="hljs-keyword">JOIN</span> classes c<br><span class="hljs-keyword">ON</span> s.class_id <span class="hljs-operator">=</span> c.id;<br></code></pre></div></td></tr></table></figure>

<p>注意INNER JOIN查询的写法是：</p>
<ol>
<li>先确定主表，仍然使用<code>FROM &lt;表1&gt;</code>的语法；</li>
<li>再确定需要连接的表，使用<code>INNER(里面的) JOIN &lt;表2&gt;</code>的语法；</li>
<li>然后确定连接条件，使用<code>ON &lt;条件...&gt;</code>，这里的条件是<code>s.class_id = c.id</code>，表示students表的class_id列与classes表的id列相同的行需要连接；</li>
<li>可选：加上WHERE子句、ORDER BY等子句。</li>
</ol>
<p>那什么是内连接（INNER JOIN）呢？先别着急，有内连接（INNER JOIN）就有外连接（OUTER JOIN）。我们把内连接查询改成外连接查询，看看效果：</p>
<figure class="highlight sql"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs SQL"><span class="hljs-keyword">SELECT</span> s.id, s.name, s.class_id, c.name class_name, s.gender, s.score<br><span class="hljs-keyword">FROM</span> students s<br><span class="hljs-keyword">RIGHT</span> <span class="hljs-keyword">OUTER</span> <span class="hljs-keyword">JOIN</span> classes c<br><span class="hljs-keyword">ON</span> s.class_id <span class="hljs-operator">=</span> c.id;<br></code></pre></div></td></tr></table></figure>

<p>执行上述RIGHT OUTER JOIN可以看到，和INNER JOIN相比，RIGHT OUTER JOIN多了一行，多出来的一行是“四班”，但是，学生相关的列如name、gender、score都为NULL。<br>这也容易理解，因为根据ON条件<code>s.class_id = c.id</code>，classes表的id=4的行正是“四班”，但是，students表中并不存在class_id=4的行。<br>有RIGHT OUTER JOIN，就有LEFT OUTER JOIN，以及FULL OUTER JOIN。它们的区别是：  </p>
<ul>
<li>INNER JOIN只返回同时存在于两张表的行数据，由于students表的class_id包含1，2，3，classes表的id包含1，2，3，4，所以，INNER JOIN根据条件<code>s.class_id = c.id</code>返回的结果集仅包含1，2，3。</li>
<li>RIGHT OUTER JOIN返回右表都存在的行。如果某一行仅在右表存在，那么结果集就会以NULL填充剩下的字段。</li>
<li>LEFT OUTER JOIN则返回左表都存在的行。如果我们给students表增加一行，并添加class_id=5，由于classes表并不存在id=5的行，所以，LEFT OUTER JOIN的结果会增加一行，对应的class_name是NULL。</li>
</ul>
<p>对于这么多种JOIN查询，到底什么使用应该用哪种呢？其实我们用图来表示结果集就一目了然了。假设查询语句是：<br><code>SELECT ... FROM tableA ??? JOIN tableB ON tableA.column1 = tableB.column2;</code><br>我们把tableA看作左表，把tableB看成右表，那么INNER JOIN是选出两张表都存在的记录：<br><img src="https://www.liaoxuefeng.com/files/attachments/1246892164662976/l" srcset="/img/loading.gif" lazyload alt="inner-join"><br>LEFT OUTER JOIN是选出左表存在的记录：<br><img src="https://www.liaoxuefeng.com/files/attachments/1246893588481376/l" srcset="/img/loading.gif" lazyload alt="left-outer-join"><br>RIGHT OUTER JOIN是选出右表存在的记录：<br><img src="https://www.liaoxuefeng.com/files/attachments/1246893609222688/l" srcset="/img/loading.gif" lazyload alt="right-outer-join"><br>FULL OUTER JOIN则是选出左右表都存在的记录：<br><img src="https://www.liaoxuefeng.com/files/attachments/1246893632359424/l" srcset="/img/loading.gif" lazyload alt="full-outer-join">  </p>
<p><strong>小结</strong><br><strong>JOIN查询需要先确定主表，然后把另一个表的数据“附加”到结果集上；  INNER JOIN是最常用的一种JOIN查询，它的语法是<code>SELECT ... FROM &lt;表1&gt; INNER JOIN &lt;表2&gt; ON &lt;条件...&gt;</code>；  JOIN查询仍然可以使用WHERE条件和ORDER BY排序。</strong></p>
<h1 id="五：-修改数据"><a href="#五：-修改数据" class="headerlink" title="五： 修改数据"></a>五： 修改数据</h1><p>关系数据库的基本操作就是增删改查，即CRUD：Create、Retrieve、Update、Delete。其中，对于查询，我们已经详细讲述了SELECT语句的详细用法。<br>而对于增、删、改，对应的SQL语句分别是：</p>
<ul>
<li>INSERT：插入新记录；</li>
<li>UPDATE：更新已有记录；</li>
<li>DELETE：删除已有记录。</li>
</ul>
<h2 id="5-1-INSERT"><a href="#5-1-INSERT" class="headerlink" title="5.1 INSERT"></a>5.1 INSERT</h2><p>当我们需要向数据库表中插入一条新记录时，就必须使用<code>INSERT</code>语句。<br>INSERT语句的基本语法是：<br><code>INSERT INTO &lt;表名&gt; (字段1, 字段2, ...) VALUES (值1, 值2, ...);</code><br>例如，我们向students表插入一条新记录，先列举出需要插入的字段名称，然后在VALUES子句中依次写出对应字段的值：<br><code>INSERT INTO students (class_id, name, gender, score) VALUES (2, &#39;大牛&#39;, &#39;M&#39;, 80);</code><br>注意到我们并没有列出id字段，也没有列出id字段对应的值，这是因为id字段是一个自增主键，它的值可以由数据库自己推算出来。此外，如果一个字段有默认值，那么在INSERT语句中也可以不出现。<br>要注意，字段顺序不必和数据库表的字段顺序一致，但值的顺序必须和字段顺序一致。也就是说，可以写:<br><code>INSERT INTO students (score, gender, name, class_id) ...</code>，但是对应的VALUES就得变成<code>(80, &#39;M&#39;, &#39;大牛&#39;, 2)</code>。<br>还可以一次性添加多条记录，只需要在VALUES子句中指定多个记录值，每个记录是由(…)包含的一组值：  </p>
<figure class="highlight sql"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs sql"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> students (class_id, name, gender, score) <span class="hljs-keyword">VALUES</span><br>  (<span class="hljs-number">1</span>, <span class="hljs-string">&#x27;大宝&#x27;</span>, <span class="hljs-string">&#x27;M&#x27;</span>, <span class="hljs-number">87</span>),<br>  (<span class="hljs-number">2</span>, <span class="hljs-string">&#x27;二宝&#x27;</span>, <span class="hljs-string">&#x27;M&#x27;</span>, <span class="hljs-number">81</span>);<br><span class="hljs-comment">--</span><br><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">FROM</span> students;<br>```  <br><br><span class="hljs-operator">*</span><span class="hljs-operator">*</span>小结<span class="hljs-operator">*</span><span class="hljs-operator">*</span><br><span class="hljs-operator">*</span><span class="hljs-operator">*</span>使用<span class="hljs-keyword">INSERT</span>，我们就可以一次向一个表中插入一条或多条记录。<span class="hljs-operator">*</span><span class="hljs-operator">*</span><br><br>## <span class="hljs-number">5.2</span> UPDATE<br><br>如果要更新数据库表中的记录，我们就必须使用UPDATE语句。  <br>UPDATE语句的基本语法是：  <br>`UPDATE <span class="hljs-operator">&lt;</span>表名<span class="hljs-operator">&gt;</span> <span class="hljs-keyword">SET</span> 字段<span class="hljs-number">1</span><span class="hljs-operator">=</span>值<span class="hljs-number">1</span>, 字段<span class="hljs-number">2</span><span class="hljs-operator">=</span>值<span class="hljs-number">2</span>, ... <span class="hljs-keyword">WHERE</span> ...;`  <br>例如，我们想更新students表id<span class="hljs-operator">=</span><span class="hljs-number">1</span>的记录的name和score这两个字段：  <br>`UPDATE students <span class="hljs-keyword">SET</span> name<span class="hljs-operator">=</span><span class="hljs-string">&#x27;大牛&#x27;</span>, score<span class="hljs-operator">=</span><span class="hljs-number">66</span> <span class="hljs-keyword">WHERE</span> id<span class="hljs-operator">=</span><span class="hljs-number">1</span>;`  <br>注意到UPDATE语句的<span class="hljs-keyword">WHERE</span>条件和<span class="hljs-keyword">SELECT</span>语句的<span class="hljs-keyword">WHERE</span>条件其实是一样的，因此完全可以一次更新多条记录：  <br>`UPDATE students <span class="hljs-keyword">SET</span> name<span class="hljs-operator">=</span><span class="hljs-string">&#x27;小牛&#x27;</span>, score<span class="hljs-operator">=</span><span class="hljs-number">77</span> <span class="hljs-keyword">WHERE</span> id<span class="hljs-operator">&gt;=</span><span class="hljs-number">5</span> <span class="hljs-keyword">AND</span> id<span class="hljs-operator">&lt;=</span><span class="hljs-number">7</span>;`  <br>在UPDATE语句中，更新字段时可以使用表达式。例如，把所有<span class="hljs-number">80</span>分以下的同学的成绩加<span class="hljs-number">10</span>分：  <br>`UPDATE students <span class="hljs-keyword">SET</span> score<span class="hljs-operator">=</span>score<span class="hljs-operator">+</span><span class="hljs-number">10</span> <span class="hljs-keyword">WHERE</span> score<span class="hljs-operator">&lt;</span><span class="hljs-number">80</span>;`  <br>其中，<span class="hljs-keyword">SET</span> score<span class="hljs-operator">=</span>score<span class="hljs-operator">+</span><span class="hljs-number">10</span>就是给当前行的score字段的值加上了<span class="hljs-number">10</span>。  <br>如果<span class="hljs-keyword">WHERE</span>条件没有匹配到任何记录，UPDATE语句不会报错，也不会有任何记录被更新。  <br>最后，要特别小心的是，UPDATE语句可以没有<span class="hljs-keyword">WHERE</span>条件，例如：  <br>`UPDATE students <span class="hljs-keyword">SET</span> score<span class="hljs-operator">=</span><span class="hljs-number">60</span>;`  <br>这时，整个表的所有记录都会被更新。所以，在执行UPDATE语句时要非常小心，最好先用<span class="hljs-keyword">SELECT</span>语句来测试<span class="hljs-keyword">WHERE</span>条件是否筛选出了期望的记录集，然后再用UPDATE更新。  <br>在使用MySQL这类真正的关系数据库时，UPDATE语句会返回更新的行数以及<span class="hljs-keyword">WHERE</span>条件匹配的行数。  <br><br><span class="hljs-operator">*</span><span class="hljs-operator">*</span>小结<span class="hljs-operator">*</span><span class="hljs-operator">*</span><br><span class="hljs-operator">*</span><span class="hljs-operator">*</span>使用UPDATE，我们就可以一次更新表中的一条或多条记录。<span class="hljs-operator">*</span><span class="hljs-operator">*</span><br><br>## <span class="hljs-number">5.3</span> <span class="hljs-keyword">DELETE</span><br><br>如果要删除数据库表中的记录，我们可以使用<span class="hljs-keyword">DELETE</span>语句。  <br><span class="hljs-keyword">DELETE</span>语句的基本语法是：  <br>`<span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">FROM</span> <span class="hljs-operator">&lt;</span>表名<span class="hljs-operator">&gt;</span> <span class="hljs-keyword">WHERE</span> ...;`<br>例如，我们想删除students表中id<span class="hljs-operator">=</span><span class="hljs-number">1</span>的记录，就需要这么写：  <br>`<span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">FROM</span> students <span class="hljs-keyword">WHERE</span> id<span class="hljs-operator">=</span><span class="hljs-number">1</span>;`  <br>注意到<span class="hljs-keyword">DELETE</span>语句的<span class="hljs-keyword">WHERE</span>条件也是用来筛选需要删除的行，因此和UPDATE类似，<span class="hljs-keyword">DELETE</span>语句也可以一次删除多条记录：  <br>`<span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">FROM</span> students <span class="hljs-keyword">WHERE</span> id<span class="hljs-operator">&gt;=</span><span class="hljs-number">5</span> <span class="hljs-keyword">AND</span> id<span class="hljs-operator">&lt;=</span><span class="hljs-number">7</span>;`  <br>如果<span class="hljs-keyword">WHERE</span>条件没有匹配到任何记录，<span class="hljs-keyword">DELETE</span>语句不会报错，也不会有任何记录被删除。  <br>最后，要特别小心的是，和UPDATE类似，不带<span class="hljs-keyword">WHERE</span>条件的<span class="hljs-keyword">DELETE</span>语句会删除整个表的数据：  <br>`<span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">FROM</span> students;`  <br>这时，整个表的所有记录都会被删除。所以，在执行<span class="hljs-keyword">DELETE</span>语句时也要非常小心，最好先用<span class="hljs-keyword">SELECT</span>语句来测试<span class="hljs-keyword">WHERE</span>条件是否筛选出了期望的记录集，然后再用<span class="hljs-keyword">DELETE</span>删除。  <br>在使用MySQL这类真正的关系数据库时，<span class="hljs-keyword">DELETE</span>语句也会返回删除的行数以及<span class="hljs-keyword">WHERE</span>条件匹配的行数。  <br><br><span class="hljs-operator">*</span><span class="hljs-operator">*</span>小结<span class="hljs-operator">*</span><span class="hljs-operator">*</span><br><span class="hljs-operator">*</span><span class="hljs-operator">*</span>使用<span class="hljs-keyword">DELETE</span>，我们就可以一次删除表中的一条或多条记录。<span class="hljs-operator">*</span><span class="hljs-operator">*</span><br><br># 六： MySQL<br><br>安装完MySQL后，除了MySQL Server，即真正的MySQL服务器外，还附赠一个MySQL Client程序。MySQL Client是一个命令行客户端，可以通过MySQL Client登录MySQL，然后，输入<span class="hljs-keyword">SQL</span>语句并执行。  <br>打开命令提示符，输入命令`mysql <span class="hljs-operator">-</span>u root <span class="hljs-operator">-</span>p`，提示输入口令。填入MySQL的root口令，如果正确，就连上了MySQL Server，同时提示符变为mysql<span class="hljs-operator">&gt;</span>：  <br>输入exit断开与MySQL Server的连接并返回到命令提示符。<br><span class="hljs-operator">&gt;</span>MySQL Client的可执行程序是mysql，MySQL Server的可执行程序是mysqld。<br><br>在MySQL Client中输入的<span class="hljs-keyword">SQL</span>语句通过TCP连接发送到MySQL Server。默认端口号是<span class="hljs-number">3306</span>，即如果发送到本机MySQL Server，地址就是<span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">3306</span>。  <br>也可以只安装MySQL Client，然后连接到远程MySQL Server。假设远程MySQL Server的IP地址是<span class="hljs-number">10.0</span><span class="hljs-number">.1</span><span class="hljs-number">.99</span>，那么就使用<span class="hljs-operator">-</span>h指定IP或域名：  <br>`mysql <span class="hljs-operator">-</span>h <span class="hljs-number">10.0</span><span class="hljs-number">.1</span><span class="hljs-number">.99</span> <span class="hljs-operator">-</span>u root <span class="hljs-operator">-</span>p 密码`  <br><br><span class="hljs-operator">*</span><span class="hljs-operator">*</span>小结<span class="hljs-operator">*</span><span class="hljs-operator">*</span><br><span class="hljs-operator">*</span><span class="hljs-operator">*</span>命令行程序mysql实际上是MySQL客户端，真正的MySQL服务器程序是mysqld，在后台运行。<span class="hljs-operator">*</span><span class="hljs-operator">*</span><br><br>## <span class="hljs-number">6.1</span> 管理MySQL<br><br>要管理MySQL，可以使用可视化图形界面[MySQL Workbench](https:<span class="hljs-operator">/</span><span class="hljs-operator">/</span>dev.mysql.com<span class="hljs-operator">/</span>downloads<span class="hljs-operator">/</span>workbench<span class="hljs-operator">/</span>)。  <br>MySQL Workbench可以用可视化的方式查询、创建和修改数据库表，但是，归根到底，MySQL Workbench是一个图形客户端，它对MySQL的操作仍然是发送<span class="hljs-keyword">SQL</span>语句并执行。因此，本质上，MySQL Workbench和MySQL Client命令行都是客户端，和MySQL交互，唯一的接口就是<span class="hljs-keyword">SQL</span>。  <br>因此，MySQL提供了大量的<span class="hljs-keyword">SQL</span>语句用于管理。虽然可以使用MySQL Workbench图形界面来直接管理MySQL，但是，很多时候，通过SSH远程连接时，只能使用<span class="hljs-keyword">SQL</span>命令，所以，了解并掌握常用的<span class="hljs-keyword">SQL</span>管理操作是必须的。  <br><br>### <span class="hljs-number">6.1</span><span class="hljs-number">.1</span> 数据库<br><br>在一个运行MySQL的服务器上，实际上可以创建多个数据库（Database）。要列出所有数据库，使用命令：  <br><br>```<span class="hljs-keyword">sql</span><br><span class="hljs-keyword">SHOW</span> DATABASES;<br></code></pre></div></td></tr></table></figure>

<p>其中，<code>information_schema</code>、<code>mysql</code>、<code>performance_schema</code>和<code>sys</code>四个是系统库，不要去改动它们。其他的是用户创建的数据库。<br>要创建一个新数据库，使用命令：  </p>
<figure class="highlight sql"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs sql"><span class="hljs-keyword">CREATE</span> DATABASE test;<br></code></pre></div></td></tr></table></figure>

<p>要删除一个数据库，使用命令：  </p>
<figure class="highlight sql"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs sql"><span class="hljs-keyword">DROP</span> DATABASE test;<br></code></pre></div></td></tr></table></figure>

<p><em>注意：删除一个数据库将导致该数据库的所有表全部被删除。</em><br>对一个数据库进行操作时，要首先将其切换为当前数据库：  </p>
<figure class="highlight sql"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs sql">USE test;<br>```  <br><br>### <span class="hljs-number">6.1</span><span class="hljs-number">.2</span> 表<br><br>列出当前数据库的所有表，使用命令：  <br><br>```<span class="hljs-keyword">sql</span><br><span class="hljs-keyword">SHOW</span> TABLES;<br>```  <br><br>要查看一个表的结构，使用命令：  <br><br>```<span class="hljs-keyword">sql</span><br><span class="hljs-keyword">DESC</span> students;<br></code></pre></div></td></tr></table></figure>

<p>还可以使用以下命令查看创建表的SQL语句：  </p>
<figure class="highlight sql"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs sql"><span class="hljs-keyword">SHOW</span> <span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> students;<br>```  <br><br>创建表使用<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span>语句，而删除表使用<span class="hljs-keyword">DROP</span> <span class="hljs-keyword">TABLE</span>语句：  <br><br>```<span class="hljs-keyword">sql</span><br><span class="hljs-keyword">DROP</span> <span class="hljs-keyword">TABLE</span> students;<br>```  <br><br>修改表就比较复杂。如果要给students表新增一列birth，使用：  <br><br>```<span class="hljs-keyword">sql</span><br><span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> students <span class="hljs-keyword">ADD</span> <span class="hljs-keyword">COLUMN</span> birth <span class="hljs-type">VARCHAR</span>(<span class="hljs-number">10</span>) <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span>;<br>```  <br><br>要修改birth列，例如把列名改为birthday，类型改为<span class="hljs-type">VARCHAR</span>(<span class="hljs-number">20</span>)：<br><br>```<span class="hljs-keyword">sql</span><br><span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> students CHANGE <span class="hljs-keyword">COLUMN</span> birth birthday <span class="hljs-type">VARCHAR</span>(<span class="hljs-number">20</span>) <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span>;<br></code></pre></div></td></tr></table></figure>

<p>要删除列，使用：</p>
<figure class="highlight sql"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs sql"><span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> students <span class="hljs-keyword">DROP</span> <span class="hljs-keyword">COLUMN</span> birthday;<br></code></pre></div></td></tr></table></figure>

<h3 id="6-1-3-退出MySQL"><a href="#6-1-3-退出MySQL" class="headerlink" title="6.1.3 退出MySQL"></a>6.1.3 退出MySQL</h3><p>使用EXIT命令退出MySQL：<br><code>EXIT</code><br>注意EXIT仅仅断开了客户端和服务器的连接，MySQL服务器仍然继续运行。  </p>
<h2 id="6-2-实用SQL语句"><a href="#6-2-实用SQL语句" class="headerlink" title="6.2 实用SQL语句"></a>6.2 实用SQL语句</h2><p>在编写SQL时，灵活运用一些技巧，可以大大简化程序逻辑。  </p>
<p><strong>一. 插入或替换</strong><br>如果我们希望插入一条新记录（INSERT），但如果记录已经存在，就先删除原记录，再插入新记录。此时，可以使用<code>REPLACE</code>语句，这样就不必先查询，再决定是否先删除再插入：<br><code>REPLACE INTO students (id, class_id, name, gender, score) VALUES (1, 1, &#39;小明&#39;, &#39;F&#39;, 99);</code><br>若id=1的记录不存在，REPLACE语句将插入新记录，否则，当前id=1的记录将被删除，然后再插入新记录。  </p>
<p><strong>二. 插入或更新</strong><br>如果我们希望插入一条新记录（INSERT），但如果记录已经存在，就更新该记录，此时，可以使用<code>INSERT INTO ... ON DUPLICATE KEY UPDATE ...</code>语句：<br><code>INSERT INTO students (id, class_id, name, gender, score) VALUES (1, 1, &#39;小明&#39;, &#39;F&#39;, 99) ON DUPLICATE KEY UPDATE name=&#39;小明&#39;, gender=&#39;F&#39;, score=99;</code><br>若id=1的记录不存在，INSERT语句将插入新记录，否则，当前id=1的记录将被更新，更新的字段由UPDATE指定。  </p>
<p><strong>三. 插入或忽略</strong><br>如果我们希望插入一条新记录（INSERT），但如果记录已经存在，就啥事也不干直接忽略，此时，可以使用<code>INSERT IGNORE INTO ...</code>语句：<br><code>INSERT IGNORE INTO students (id, class_id, name, gender, score) VALUES (1, 1, &#39;小明&#39;, &#39;F&#39;, 99);</code><br>若id=1的记录不存在，INSERT语句将插入新记录，否则，不执行任何操作。  </p>
<p><strong>四. 快照</strong><br>如果想要对一个表进行快照，即复制一份当前表的数据到一个新表，可以结合CREATE TABLE和SELECT：  </p>
<figure class="highlight sql"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs sql"><span class="hljs-comment">-- 对class_id=1的记录进行快照，并存储为新表students_of_class1:  </span><br><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> students_of_class1 <span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">FROM</span> students <span class="hljs-keyword">WHERE</span> class_id<span class="hljs-operator">=</span><span class="hljs-number">1</span>;<br></code></pre></div></td></tr></table></figure>
<p>新创建的表结构和SELECT使用的表结构完全一致。</p>
<p><strong>五. 写入查询结果集</strong><br>如果查询结果集需要写入到表中，可以结合INSERT和SELECT，将SELECT语句的结果集直接插入到指定表中。<br>例如，创建一个统计成绩的表statistics，记录各班的平均成绩：  </p>
<figure class="highlight sql"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> statistics (<br>    id <span class="hljs-type">BIGINT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span> AUTO_INCREMENT,<br>    class_id <span class="hljs-type">BIGINT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span>,<br>    average <span class="hljs-keyword">DOUBLE</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span>,<br>    <span class="hljs-keyword">PRIMARY</span> KEY (id)<br>);<br></code></pre></div></td></tr></table></figure>

<p>然后，我们就可以用一条语句写入各班的平均成绩：  </p>
<figure class="highlight sql"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs sql"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> statistics (class_id, average) <span class="hljs-keyword">SELECT</span> class_id, <span class="hljs-built_in">AVG</span>(score) <span class="hljs-keyword">FROM</span> students <span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> class_id;<br></code></pre></div></td></tr></table></figure>

<p>确保INSERT语句的列和SELECT语句的列能一一对应，就可以在statistics表中直接保存查询的结果：  </p>
<figure class="highlight sql"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">FROM</span> statistics;<br></code></pre></div></td></tr></table></figure>

<p><strong>六. 强制使用指定索引</strong><br>在查询的时候，数据库系统会自动分析查询语句，并选择一个最合适的索引。但是很多时候，数据库系统的查询优化器并不一定总是能使用最优索引。如果我们知道如何选择索引，可以使用FORCE INDEX强制查询使用指定的索引。例如：  </p>
<figure class="highlight sql"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">FROM</span> students FORCE INDEX (idx_class_id) <span class="hljs-keyword">WHERE</span> class_id <span class="hljs-operator">=</span> <span class="hljs-number">1</span> <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> id <span class="hljs-keyword">DESC</span>;<br></code></pre></div></td></tr></table></figure>

<p>指定索引的前提是索引idx_class_id必须存在。  </p>
<h1 id="七：-事务"><a href="#七：-事务" class="headerlink" title="七： 事务"></a>七： 事务</h1><p>在执行SQL语句的时候，某些业务要求，一系列操作必须全部执行，而不能仅执行一部分。例如，一个转账操作：  </p>
<figure class="highlight sql"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs sql"><span class="hljs-comment">-- 从id=1的账户给id=2的账户转账100元</span><br><span class="hljs-comment">-- 第一步：将id=1的A账户余额减去100</span><br>UPDATE accounts <span class="hljs-keyword">SET</span> balance <span class="hljs-operator">=</span> balance <span class="hljs-operator">-</span> <span class="hljs-number">100</span> <span class="hljs-keyword">WHERE</span> id <span class="hljs-operator">=</span> <span class="hljs-number">1</span>;<br><span class="hljs-comment">-- 第二步：将id=2的B账户余额加上100</span><br>UPDATE accounts <span class="hljs-keyword">SET</span> balance <span class="hljs-operator">=</span> balance <span class="hljs-operator">+</span> <span class="hljs-number">100</span> <span class="hljs-keyword">WHERE</span> id <span class="hljs-operator">=</span> <span class="hljs-number">2</span>;<br></code></pre></div></td></tr></table></figure>

<p>这两条SQL语句必须全部执行，或者，由于某些原因，如果第一条语句成功，第二条语句失败，就必须全部撤销。<br>这种把<strong>多条语句作为一个整体进行操作的功能，被称为数据库事务</strong>。数据库事务可以确保该事务范围内的所有操作都可以全部成功或者全部失败。如果事务失败，那么效果就和没有执行这些SQL一样，不会对数据库数据有任何改动。<br>可见，数据库事务具有ACID这4个特性：</p>
<ol>
<li>A：Atomic，原子性，将所有SQL作为原子工作单元执行，要么全部执行，要么全部不执行；</li>
<li>C：Consistent，一致性，事务完成后，所有数据的状态都是一致的，即A账户只要减去了100，B账户则必定加上了100；</li>
<li>I：Isolation，隔离性，如果有多个事务并发执行，每个事务作出的修改必须与其他事务隔离；</li>
<li>D：Duration，持久性，即事务完成后，对数据库数据的修改被持久化存储。</li>
</ol>
<p>对于单条SQL语句，数据库系统自动将其作为一个事务执行，这种事务被称为隐式事务。<br>要手动把多条SQL语句作为一个事务执行，使用<code>BEGIN</code>开启一个事务，使用<code>COMMIT</code>提交一个事务，这种事务被称为<strong>显式事务</strong>，例如，把上述的转账操作作为一个显式事务：</p>
<figure class="highlight sql"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs sql"><span class="hljs-keyword">BEGIN</span>;<br>UPDATE accounts <span class="hljs-keyword">SET</span> balance <span class="hljs-operator">=</span> balance <span class="hljs-operator">-</span> <span class="hljs-number">100</span> <span class="hljs-keyword">WHERE</span> id <span class="hljs-operator">=</span> <span class="hljs-number">1</span>;<br>UPDATE accounts <span class="hljs-keyword">SET</span> balance <span class="hljs-operator">=</span> balance <span class="hljs-operator">+</span> <span class="hljs-number">100</span> <span class="hljs-keyword">WHERE</span> id <span class="hljs-operator">=</span> <span class="hljs-number">2</span>;<br><span class="hljs-keyword">COMMIT</span>;<br></code></pre></div></td></tr></table></figure>

<p>很显然多条SQL语句要想作为一个事务执行，就必须使用显式事务。<br>COMMIT是指提交事务，即试图把事务内的所有SQL所做的修改永久保存。如果COMMIT语句执行失败了，整个事务也会失败。<br>有些时候，我们希望主动让事务失败，这时，可以用ROLLBACK回滚事务，整个事务会失败：  </p>
<figure class="highlight sql"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs sql"><span class="hljs-keyword">BEGIN</span>;<br>UPDATE accounts <span class="hljs-keyword">SET</span> balance <span class="hljs-operator">=</span> balance <span class="hljs-operator">-</span> <span class="hljs-number">100</span> <span class="hljs-keyword">WHERE</span> id <span class="hljs-operator">=</span> <span class="hljs-number">1</span>;<br>UPDATE accounts <span class="hljs-keyword">SET</span> balance <span class="hljs-operator">=</span> balance <span class="hljs-operator">+</span> <span class="hljs-number">100</span> <span class="hljs-keyword">WHERE</span> id <span class="hljs-operator">=</span> <span class="hljs-number">2</span>;<br><span class="hljs-keyword">ROLLBACK</span>;<br></code></pre></div></td></tr></table></figure>

<p>数据库事务是由数据库系统保证的，我们只需要根据业务逻辑使用它就可以。  </p>
<p><strong>隔离级别</strong></p>
<p>对于两个并发执行的事务，如果涉及到操作同一条记录的时候，可能会发生问题。因为并发操作会带来数据的不一致性，包括脏读、不可重复读、幻读等。数据库系统提供了隔离级别来让我们有针对性地选择事务的隔离级别，避免数据不一致的问题。<br>SQL标准定义了4种隔离级别，分别对应可能出现的数据不一致的情况：  </p>
<table>
<thead>
<tr>
<th>Isolation Level</th>
<th>脏读（Dirty Read）</th>
<th>不可重复读（Non Repeatable Read）</th>
<th>幻读（Phantom Read）</th>
</tr>
</thead>
<tbody><tr>
<td>Read Uncommitted</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>Read Committed</td>
<td>-</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>Repeatable Read</td>
<td>-</td>
<td>-</td>
<td>Yes</td>
</tr>
<tr>
<td>Serializable</td>
<td>-</td>
<td>-</td>
<td>-</td>
</tr>
</tbody></table>
<p>我们会依次介绍4种隔离级别的数据一致性问题。  </p>
<p><strong>小结</strong><br><strong>数据库事务具有ACID特性，用来保证多条SQL的全部执行。</strong></p>
<h2 id="7-1-Read-Uncommitted"><a href="#7-1-Read-Uncommitted" class="headerlink" title="7.1 Read Uncommitted"></a>7.1 Read Uncommitted</h2><p>Read Uncommitted是隔离级别最低的一种事务级别。在这种隔离级别下，一个事务会读到另一个事务更新后但未提交的数据，如果另一个事务回滚，那么当前事务读到的数据就是脏数据，这就是脏读（Dirty Read）。<br>我们来看一个例子。首先，我们准备好students表的数据，该表仅一行记录。<br>然后，分别开启两个MySQL客户端连接，按顺序依次执行事务A和事务B：</p>
<table>
<thead>
<tr>
<th>时刻</th>
<th>事务A</th>
<th>事务B</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;</td>
<td>SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;</td>
</tr>
<tr>
<td>2</td>
<td>BEGIN;</td>
<td>BEGIN;</td>
</tr>
<tr>
<td>3</td>
<td>UPDATE students SET name = ‘Bob’ WHERE id = 1;</td>
<td></td>
</tr>
<tr>
<td>4</td>
<td></td>
<td>SELECT * FROM students WHERE id = 1;</td>
</tr>
<tr>
<td>5</td>
<td>ROLLBACK;</td>
<td></td>
</tr>
<tr>
<td>6</td>
<td></td>
<td>SELECT * FROM students WHERE id = 1;</td>
</tr>
<tr>
<td>7</td>
<td></td>
<td>COMMIT;</td>
</tr>
</tbody></table>
<p>当事务A执行完第3步时，它更新了id=1的记录，但并未提交，而事务B在第4步读取到的数据就是未提交的数据。<br>随后，事务A在第5步进行了回滚，事务B再次读取id=1的记录，发现和上一次读取到的数据不一致，这就是脏读。<br>可见，在Read Uncommitted隔离级别下，一个事务可能读取到另一个事务更新但未提交的数据，这个数据有可能是脏数据。  </p>
<h2 id="7-2-Read-Committed"><a href="#7-2-Read-Committed" class="headerlink" title="7.2 Read Committed"></a>7.2 Read Committed</h2><p>在Read Committed隔离级别下，一个事务可能会遇到不可重复读（Non Repeatable Read）的问题。<br>不可重复读是指，在一个事务内，多次读同一数据，在这个事务还没有结束时，如果另一个事务恰好修改了这个数据，那么，在第一个事务中，两次读取的数据就可能不一致。<br>我们仍然先准备好students表的数据，然后，分别开启两个MySQL客户端连接，按顺序依次执行事务A和事务B：  </p>
<table>
<thead>
<tr>
<th>时刻</th>
<th>事务A</th>
<th>事务B</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>SET TRANSACTION ISOLATION LEVEL READ COMMITTED;</td>
<td>SET TRANSACTION ISOLATION LEVEL READ COMMITTED;</td>
</tr>
<tr>
<td>2</td>
<td>BEGIN;</td>
<td>BEGIN;</td>
</tr>
<tr>
<td>3</td>
<td></td>
<td>SELECT * FROM students WHERE id = 1;</td>
</tr>
<tr>
<td>4</td>
<td>UPDATE students SET name = ‘Bob’ WHERE id = 1;</td>
<td></td>
</tr>
<tr>
<td>5</td>
<td>COMMIT;</td>
<td></td>
</tr>
<tr>
<td>6</td>
<td></td>
<td>SELECT * FROM students WHERE id = 1;</td>
</tr>
<tr>
<td>7</td>
<td></td>
<td>COMMIT;</td>
</tr>
</tbody></table>
<p>当事务B第一次执行第3步的查询时，得到的结果是Alice，随后，由于事务A在第4步更新了这条记录并提交，所以，事务B在第6步再次执行同样的查询时，得到的结果就变成了Bob，因此，在Read Committed隔离级别下，事务不可重复读同一条记录，因为很可能读到的结果不一致。  </p>
<h2 id="7-3-Repeatable-Read"><a href="#7-3-Repeatable-Read" class="headerlink" title="7.3 Repeatable Read"></a>7.3 Repeatable Read</h2><p>在Repeatable Read隔离级别下，一个事务可能会遇到幻读（Phantom Read）的问题。<br>幻读是指，在一个事务中，第一次查询某条记录，发现没有，但是，当试图更新这条不存在的记录时，竟然能成功，并且，再次读取同一条记录，它就神奇地出现了。<br>我们仍然先准备好students表的数据，然后，分别开启两个MySQL客户端连接，按顺序依次执行事务A和事务B：  </p>
<table>
<thead>
<tr>
<th>时刻</th>
<th>事务A</th>
<th>事务B</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;</td>
<td>SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;</td>
</tr>
<tr>
<td>2</td>
<td>BEGIN;</td>
<td>BEGIN;</td>
</tr>
<tr>
<td>3</td>
<td></td>
<td>SELECT * FROM students WHERE id = 99;</td>
</tr>
<tr>
<td>4</td>
<td>INSERT INTO students (id, name) VALUES (99, ‘Bob’);</td>
<td></td>
</tr>
<tr>
<td>5</td>
<td>COMMIT;</td>
<td></td>
</tr>
<tr>
<td>6</td>
<td></td>
<td>SELECT * FROM students WHERE id = 99;</td>
</tr>
<tr>
<td>7</td>
<td></td>
<td>UPDATE students SET name = ‘Alice’ WHERE id = 99;</td>
</tr>
<tr>
<td>8</td>
<td></td>
<td>SELECT * FROM students WHERE id = 99;</td>
</tr>
<tr>
<td>9</td>
<td></td>
<td>COMMIT;</td>
</tr>
</tbody></table>
<p>事务B在第3步第一次读取id=99的记录时，读到的记录为空，说明不存在id=99的记录。随后，事务A在第4步插入了一条id=99的记录并提交。事务B在第6步再次读取id=99的记录时，读到的记录仍然为空，但是，事务B在第7步试图更新这条不存在的记录时，竟然成功了，并且，事务B在第8步再次读取id=99的记录时，记录出现了。<br>可见，幻读就是没有读到的记录，以为不存在，但其实是可以更新成功的，并且，更新成功后，再次读取，就出现了。  </p>
<h2 id="7-4-Serializable"><a href="#7-4-Serializable" class="headerlink" title="7.4 Serializable"></a>7.4 Serializable</h2><p>Serializable是最严格的隔离级别。在Serializable隔离级别下，所有事务按照次序依次执行，因此，脏读、不可重复读、幻读都不会出现。<br>虽然Serializable隔离级别下的事务具有最高的安全性，但是，由于事务是串行执行，所以效率会大大下降，应用程序的性能会急剧降低。如果没有特别重要的情景，一般都不会使用Serializable隔离级别。  </p>
<p><strong>默认隔离级别</strong><br><strong>如果没有指定隔离级别，数据库就会使用默认的隔离级别。在MySQL中，如果使用InnoDB，默认的隔离级别是Repeatable Read。</strong>  </p>

            </div>
            <hr>
            <div>
              <div class="post-metas mb-3">
                
                  <div class="post-meta mr-3">
                    <i class="iconfont icon-category"></i>
                    
                      <a class="hover-with-bg" href="/categories/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/">读书笔记</a>
                    
                      <a class="hover-with-bg" href="/categories/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/MYSQL/">MYSQL</a>
                    
                  </div>
                
                
                  <div class="post-meta">
                    <i class="iconfont icon-tags"></i>
                    
                      <a class="hover-with-bg" href="/tags/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/">读书笔记</a>
                    
                      <a class="hover-with-bg" href="/tags/MySQL/">MySQL</a>
                    
                  </div>
                
              </div>
              
                <p class="note note-warning">
                  
                    本博客所有文章除特别声明外，均采用 <a target="_blank" href="https://creativecommons.org/licenses/by-sa/4.0/deed.zh" rel="nofollow noopener noopener">CC BY-SA 4.0 协议</a> ，转载请注明出处！
                  
                </p>
              
              
                <div class="post-prevnext">
                  <article class="post-prev col-6">
                    
                    
                      <a href="/2020/01/29/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/%E5%A4%A7%E8%AF%9D%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E7%AC%94%E8%AE%B0/%E7%AC%AC8%E7%AB%A0%20%E6%9F%A5%E6%89%BE/">
                        <i class="iconfont icon-arrowleft"></i>
                        <span class="hidden-mobile">大话数据结构第八章 查找</span>
                        <span class="visible-mobile">上一篇</span>
                      </a>
                    
                  </article>
                  <article class="post-next col-6">
                    
                    
                      <a href="/2020/01/08/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/%E5%A4%A7%E8%AF%9D%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E7%AC%94%E8%AE%B0/%E7%AC%AC7%E7%AB%A0%20%E5%9B%BE/">
                        <span class="hidden-mobile">大话数据结构第七章 图</span>
                        <span class="visible-mobile">下一篇</span>
                        <i class="iconfont icon-arrowright"></i>
                      </a>
                    
                  </article>
                </div>
              
            </div>

            
              <!-- Comments -->
              <article class="comments" id="comments" lazyload>
                
                  
                
                
  <div id="valine"></div>
  <script type="text/javascript">
    Fluid.utils.loadComments('#valine', function() {
      Fluid.utils.createScript('https://cdn.jsdelivr.net/npm/valine@1.4.14/dist/Valine.min.js', function () {
        new Valine({
          el: "#valine",
          app_id: "YzLqNtMw1YEwwACli1FUsIUM-gzGzoHsz",
          app_key: "HLUt5izfTvTcbEbOrA59W92a",
          placeholder: "畅所欲言...",
          path: window.location.pathname,
          avatar: "robohash",
          meta: ["nick","mail","link"],
          pageSize: "10",
          lang: "zh-CN",
          highlight: true,
          recordIP: false,
          serverURLs: "",
        });
      });
    });
  </script>
  <noscript>Please enable JavaScript to view the comments</noscript>


              </article>
            
          </article>
        </div>
      </div>
    </div>
    
      <div class="d-none d-lg-block col-lg-2 toc-container" id="toc-ctn">
        <div id="toc">
  <p class="toc-header"><i class="iconfont icon-list"></i>&nbsp;目录</p>
  <div class="toc-body" id="toc-body"></div>
</div>

      </div>
    
  </div>
</div>

<!-- Custom -->

  <div class="col-lg-7 mx-auto nopadding-x-md">
    <div class="container custom post-custom mx-auto">
      <img src="https://closer_laps.coding.net/p/picture/d/picture/git/raw/master/pay/pay.png" srcset="/img/loading.gif" lazyload class="rounded mx-auto d-block mt-3" style="width:355.4px; height:200px;">
    </div>
  </div>


    

    
      <a id="scroll-top-button" href="#" role="button">
        <i class="iconfont icon-arrowup" aria-hidden="true"></i>
      </a>
    

    
      <div class="modal fade" id="modalSearch" tabindex="-1" role="dialog" aria-labelledby="ModalLabel"
     aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header text-center">
        <h4 class="modal-title w-100 font-weight-bold">搜索</h4>
        <button type="button" id="local-search-close" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body mx-3">
        <div class="md-form mb-5">
          <input type="text" id="local-search-input" class="form-control validate">
          <label data-error="x" data-success="v"
                 for="local-search-input">关键词</label>
        </div>
        <div class="list-group" id="local-search-result"></div>
      </div>
    </div>
  </div>
</div>
    

    
  </main>

  <footer class="text-center mt-5 py-3">
  <div class="footer-content">
     <a href="https://hexo.io" target="_blank" rel="nofollow noopener"><span>Hexo</span></a> <i class="iconfont icon-love"></i> <a href="https://github.com/fluid-dev/hexo-theme-fluid" target="_blank" rel="nofollow noopener"><span>Fluid</span></a> 
  </div>
  
  <div class="statistics">
    
    

    
      
        <!-- LeanCloud 统计PV -->
        <span id="leancloud-site-pv-container" style="display: none">
            总访问量 
            <span id="leancloud-site-pv"></span>
             次
          </span>
      
      
        <!-- LeanCloud 统计UV -->
        <span id="leancloud-site-uv-container" style="display: none">
            总访客数 
            <span id="leancloud-site-uv"></span>
             人
          </span>
      

    
  </div>


  
  <!-- 备案信息 -->
  <div class="beian">
    <span>
      <a href="http://beian.miit.gov.cn/" target="_blank" rel="nofollow noopener">
        苏ICP备20032307号
      </a>
    </span>
    
      
        <span>
          <a
            href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=32020602001023"
            rel="nofollow noopener"
            class="beian-police"
            target="_blank"
          >
            
              <span style="visibility: hidden; width: 0">|</span>
              <img src="/img/police_beian.png" srcset="/img/loading.gif" lazyload alt="police-icon"/>
            
            <span>苏公网安备 32020602001023号</span>
          </a>
        </span>
      
    
  </div>


  
</footer>


  <!-- SCRIPTS -->
  
  <script  src="https://cdn.jsdelivr.net/npm/nprogress@0.2.0/nprogress.min.js" ></script>
  <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/nprogress@0.2.0/nprogress.min.css" />

  <script>
    NProgress.configure({"showSpinner":false,"trickleSpeed":100})
    NProgress.start()
    window.addEventListener('load', function() {
      NProgress.done();
    })
  </script>


<script  src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js" ></script>
<script  src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.min.js" ></script>
<script  src="/js/events.js" ></script>
<script  src="/js/plugins.js" ></script>

<!-- Plugins -->


  
    <script  src="/js/img-lazyload.js" ></script>
  



  



  <script  src="https://cdn.jsdelivr.net/npm/tocbot@4.12.2/dist/tocbot.min.js" ></script>



  <script  src="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.js" ></script>



  <script  src="https://cdn.jsdelivr.net/npm/anchor-js@4.3.0/anchor.min.js" ></script>



  <script defer src="https://cdn.jsdelivr.net/npm/clipboard@2.0.8/dist/clipboard.min.js" ></script>




  <script defer src="/js/leancloud.js" ></script>



  <script  src="https://cdn.jsdelivr.net/npm/typed.js@2.0.11/lib/typed.min.js" ></script>
  <script>
    (function (window, document) {
      var typing = Fluid.plugins.typing;
      var title = document.getElementById('subtitle').title;
      
      typing(title)
      
    })(window, document);
  </script>



  <script  src="/js/local-search.js" ></script>
  <script>
    (function () {
      var path = "/local-search.xml";
      $('#local-search-input').on('click', function() {
        searchFunc(path, 'local-search-input', 'local-search-result');
      });
      $('#modalSearch').on('shown.bs.modal', function() {
        $('#local-search-input').focus();
      });
    })()
  </script>





  

  
    <!-- MathJax -->
    <script>
      MathJax = {
        tex: {
          inlineMath: [['$', '$'], ['\\(', '\\)']]
        },
        options: {
          renderActions: {
            findScript: [10, doc => {
              document.querySelectorAll('script[type^="math/tex"]').forEach(node => {
                const display = !!node.type.match(/; *mode=display/);
                const math = new doc.options.MathItem(node.textContent, doc.inputJax[0], display);
                const text = document.createTextNode('');
                node.parentNode.replaceChild(text, node);
                math.start = { node: text, delim: '', n: 0 };
                math.end = { node: text, delim: '', n: 0 };
                doc.math.push(math);
              });
            }, '', false],
            insertedScript: [200, () => {
              document.querySelectorAll('mjx-container').forEach(node => {
                let target = node.parentNode;
                if (target.nodeName.toLowerCase() === 'li') {
                  target.parentNode.classList.add('has-jax');
                }
              });
            }, '', false]
          }
        }
      };
    </script>

    <script async src="https://cdn.jsdelivr.net/npm/mathjax@3.1.2/es5/tex-svg.js" ></script>

  








  
    <!-- Baidu Analytics -->
    <script defer>
      var _hmt = _hmt || [];
      (function () {
        var hm = document.createElement("script");
        hm.src = "https://hm.baidu.com/hm.js?608f2baddd361128381ad2bf9377bf89";
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(hm, s);
      })();
    </script>
  

  

  

  

  

  





<!-- 主题的启动项 保持在最底部 -->
<script  src="/js/boot.js" ></script>


</body>
</html>
